/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the EternalTech source code.

 The EternalTech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The EternalTech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the EternalTech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// cg_gameState.cpp - ...
//


#include "cg_local.h"


/*
 ==============================================================================

 CONFIG STRINGS

 ==============================================================================
*/


/*
 ==================
 CG_ParseConfig

 Called during level loading to set the initial values from config strings
 ==================
*/
void CG_ParseConfig (){

	// Check the game version
	if (Str_Compare(cgs.configStrings[CS_VERSION], GAME_VERSION))
		G_Error("Client/Server game mismatch (%s != %s)", cgs.configStrings[CS_VERSION], GAME_VERSION);

	// Get the level name
	Str_Copy(cgs.levelName, cgs.configStrings[CS_LEVEL_NAME], sizeof(cgs.levelName));

	// Get the gravity
	cgs.gravity = Str_ToFloat(cgs.configStrings[CS_GRAVITY]);

	// Get the music track
	cgs.musicTrack = Str_ToInteger(cgs.configStrings[CS_MUSIC]);
}

/*
 ==================
 
 TODO: add more strings if needed
 ==================
*/
void CG_SetConfigString (int index, const char *string){

	if (index < 0 || index >= MAX_CONFIG_STRINGS)
		G_Error("CG_SetConfigString: index out of range");

	if (!string)
		string = "";

	if (cgs.configStrings[index]){
		// Don't bother updating if no change
		if (!Str_Compare(cgs.configStrings[index], string))
			return;

		glqMemory->Free(cgs.configStrings[index]);
	}

	// Copy the string
	cgs.configStrings[index] = glqMemory->DupString(string, TAG_CLIENT);

	// Do something appropriate with it
	if (index >= CS_MODELS && index < CS_MODELS + MAX_GAME_MODELS){
		cgs.gameModels[index - CS_MODELS] = glqRenderer->RegisterModel(cgs.configStrings[index]);
		return;
	}

	if (index >= CS_MATERIALS && index < CS_MATERIALS + MAX_GAME_MATERIALS){
		cgs.gameMaterials[index - CS_MATERIALS] = glqRenderer->RegisterMaterialLight(cgs.configStrings[index]);
		return;
	}

	if (index >= CS_SOUND_SHADERS && index < CS_SOUND_SHADERS + MAX_GAME_SOUND_SHADERS){
		cgs.gameSoundShaders[index - CS_SOUND_SHADERS] = glqSound->RegisterSoundShader(cgs.configStrings[index]);
		return;
	}
}


/*
 ==============================================================================

 BASELINES (WRITING)

 ==============================================================================
*/


/*
 ==================
 
 ==================
*/
static void CG_WriteBaseline (msg_t *msg, entityState_t *baseline){

}


/*
 ==============================================================================

 BASELINES (PARSING)

 ==============================================================================
*/


/*
 ==================
 CG_ParseBaseline_General
 ==================
*/
static void CG_ParseBaseline_General (msg_t *msg, entityState_t *baseline){

	general_t	*general = (general_t *)baseline->buffer;

	general->contents = glqMsgSystem->ReadDeltaLong(msg, 0);
	general->bounds = glqMsgSystem->ReadDeltaLong(msg, 0);
	general->model = glqMsgSystem->ReadDeltaBits(msg, 0, ES_MODEL_BITS);
	general->clipModel = glqMsgSystem->ReadDeltaBits(msg, 0, ES_CLIP_MODEL_BITS);
	general->material = glqMsgSystem->ReadDeltaBits(msg, 0, ES_MATERIAL_BITS);
	general->materialParms[0] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->materialParms[1] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->materialParms[2] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->materialParms[3] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->materialParms[4] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->materialParms[5] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->materialParms[6] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->materialParms[7] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->sound = glqMsgSystem->ReadDeltaBits(msg, 0, ES_SOUND_SHADER_BITS);
	general->soundTime = glqMsgSystem->ReadDeltaLong(msg, 0);
	general->soundParms[0] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->soundParms[1] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->soundParms[2] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->soundParms[3] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->soundParms[4] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->soundParms[5] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->soundParms[6] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	general->soundParms[7] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
}

/*
 ==================
 
 ==================
*/
static void CG_ParseBaseline_Mover (msg_t *msg, entityState_t *baseline){

	mover_t	*mover = (mover_t *)baseline->buffer;

	mover->model = glqMsgSystem->ReadDeltaBits(msg, 0, ES_MODEL_BITS);
}

/*
 ==================
 
 ==================
*/
static void CG_ParseBaseline_Beam (msg_t *msg, entityState_t *baseline){

}

/*
 ==================
 CG_ParseBaseline_Light
 ==================
*/
static void CG_ParseBaseline_Light (msg_t *msg, entityState_t *baseline){

	light_t	*light = (light_t *)baseline->buffer;

	light->type = glqMsgSystem->ReadDeltaBits(msg, 0, 2);
	light->center = glqMsgSystem->ReadDeltaVec3(msg, vec3_origin);
	light->radius = glqMsgSystem->ReadDeltaVec3(msg, vec3_origin);
	light->xMin = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->xMax = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->yMin = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->yMax = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->zNear = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->zFar = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->noShadows = glqMsgSystem->ReadBool(msg);
	light->shadowBias = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->shadowSlopeScaleBias = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->shadowSoftness = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->fogDistance = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->fogHeight = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->volumeIntensity = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->volumeShadows = glqMsgSystem->ReadBool(msg);
	light->volumeInteractions = glqMsgSystem->ReadBool(msg);
	light->detailLevel = glqMsgSystem->ReadDeltaBits(msg, 0, 2);
	light->material = glqMsgSystem->ReadDeltaBits(msg, 0, ES_MATERIAL_BITS);
	light->materialParms[0] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->materialParms[1] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->materialParms[2] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->materialParms[3] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->materialParms[4] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->materialParms[5] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->materialParms[6] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	light->materialParms[7] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
}

/*
 ==================
 CG_ParseBaseline_Speaker
 ==================
*/
static void CG_ParseBaseline_Speaker (msg_t *msg, entityState_t *baseline){

	speaker_t	*speaker = (speaker_t *)baseline->buffer;

	speaker->direction = glqMsgSystem->ReadDeltaNormal(msg, vec3_origin);
	speaker->sound = glqMsgSystem->ReadDeltaBits(msg, 0, ES_SOUND_SHADER_BITS);
	speaker->soundTime = glqMsgSystem->ReadDeltaLong(msg, 0);
	speaker->soundParms[0] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	speaker->soundParms[1] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	speaker->soundParms[2] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	speaker->soundParms[3] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	speaker->soundParms[4] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	speaker->soundParms[5] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	speaker->soundParms[6] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	speaker->soundParms[7] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
}

/*
 ==================
 CG_ParseBaseline_Player
 ==================
*/
static void CG_ParseBaseline_Player (msg_t *msg, entityState_t *baseline){

	player_t	*player = (player_t *)baseline->buffer;

	player->bounds = glqMsgSystem->ReadDeltaLong(msg, 0);

	player->model = glqMsgSystem->ReadDeltaBits(msg, 0, ES_MODEL_BITS);

	player->sound = glqMsgSystem->ReadDeltaBits(msg, 0, ES_SOUND_SHADER_BITS);
	player->soundTime = glqMsgSystem->ReadDeltaLong(msg, 0);
	player->soundParms[0] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	player->soundParms[1] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	player->soundParms[2] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	player->soundParms[3] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	player->soundParms[4] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	player->soundParms[5] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	player->soundParms[6] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	player->soundParms[7] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
}

/*
 ==================
 
 ==================
*/
static void CG_ParseBaseline_Item (msg_t *msg, entityState_t *baseline){

	item_t	*item = (item_t *)baseline->buffer;

	item->bounds = glqMsgSystem->ReadDeltaLong(msg, 0);

	item->model = glqMsgSystem->ReadDeltaBits(msg, 0, ES_MODEL_BITS);

	item->sound = glqMsgSystem->ReadDeltaBits(msg, 0, ES_SOUND_SHADER_BITS);
	item->soundTime = glqMsgSystem->ReadDeltaLong(msg, 0);
	item->soundParms[0] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	item->soundParms[1] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	item->soundParms[2] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	item->soundParms[3] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	item->soundParms[4] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	item->soundParms[5] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	item->soundParms[6] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	item->soundParms[7] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
}

/*
 ==================
 
 ==================
*/
static void CG_ParseBaseline_Monster (msg_t *msg, entityState_t *baseline){

	monster_t	*monster = (monster_t *)baseline->buffer;

	monster->bounds = glqMsgSystem->ReadDeltaLong(msg, 0);

	monster->model = glqMsgSystem->ReadDeltaBits(msg, 0, ES_MODEL_BITS);

	monster->sound = glqMsgSystem->ReadDeltaBits(msg, 0, ES_SOUND_SHADER_BITS);
	monster->soundTime = glqMsgSystem->ReadDeltaLong(msg, 0);
	monster->soundParms[0] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	monster->soundParms[1] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	monster->soundParms[2] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	monster->soundParms[3] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	monster->soundParms[4] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	monster->soundParms[5] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	monster->soundParms[6] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
	monster->soundParms[7] = glqMsgSystem->ReadDeltaFloat(msg, 0.0f);
}

/*
 ==================
 
 ==================
*/
static void CG_ParseBaseline_Projectile (msg_t *msg, entityState_t *baseline){

	projectile_t	*projectile = (projectile_t *)baseline->buffer;
}

/*
 ==================
 
 ==================
*/
static void CG_ParseBaseline (msg_t *msg, entityState_t *baseline, int number){

	int		i;

	baseline->number = number;
	baseline->type = glqMsgSystem->ReadBits(msg, ES_TYPE_BITS);
	baseline->flags = glqMsgSystem->ReadBits(msg, ES_FLAGS_BITS);

	// Parse the position and orientation
	baseline->origin.type = glqMsgSystem->ReadDeltaByte(msg, 0);
	baseline->origin.time = glqMsgSystem->ReadDeltaLong(msg, 0);
	baseline->origin.duration = glqMsgSystem->ReadDeltaLong(msg, 0);
	baseline->origin.base = glqMsgSystem->ReadDeltaVec3(msg, vec3_origin);
	baseline->origin.delta = glqMsgSystem->ReadDeltaVec3(msg, vec3_origin);

	baseline->angles.type = glqMsgSystem->ReadDeltaByte(msg, 0);
	baseline->angles.time = glqMsgSystem->ReadDeltaLong(msg, 0);
	baseline->angles.duration = glqMsgSystem->ReadDeltaLong(msg, 0);
	baseline->angles.base = glqMsgSystem->ReadDeltaAngles(msg, angles_zero);
	baseline->angles.delta = glqMsgSystem->ReadDeltaAngles(msg, angles_zero);

	// Parse the binding information
	baseline->bindMaster = glqMsgSystem->ReadDeltaBits(msg, 0, ES_BIND_MASTER_BITS);
	baseline->bindOrientated = glqMsgSystem->ReadBool(msg);

	// Parse the event queue
	baseline->eventSequence = glqMsgSystem->ReadDeltaLong(msg, 0);

	for (i = 0; i < MAX_EVENTS; i++){
		baseline->eventTypes[i] = glqMsgSystem->ReadDeltaByte(msg, 0);
		baseline->eventParms[i] = glqMsgSystem->ReadDeltaWord(msg, 0);
	}

	// Write the state buffer
	switch (baseline->type){
	case ET_GENERAL:
		CG_ParseBaseline_General(msg, baseline);
		break;
	case ET_MOVER:
		CG_ParseBaseline_Mover(msg, baseline);
		break;
	case ET_BEAM:
		CG_ParseBaseline_Beam(msg, baseline);
		break;
	case ET_LIGHT:
		CG_ParseBaseline_Light(msg, baseline);
		break;
	case ET_SPEAKER:
		CG_ParseBaseline_Speaker(msg, baseline);
		break;
	case ET_PLAYER:
		CG_ParseBaseline_Player(msg, baseline);
		break;
	case ET_ITEM:
		CG_ParseBaseline_Item(msg, baseline);
		break;
	case ET_MONSTER:
		CG_ParseBaseline_Monster(msg, baseline);
		break;
	case ET_PROJECTILE:
		CG_ParseBaseline_Projectile(msg, baseline);
		break;
	default:
		G_Error("SG_WriteBaseline: bad entity type (%i)", baseline->type);
	}
}


/*
 ==============================================================================

 GAME STATE

 ==============================================================================
*/


/*
 ==================
 
 NOTE: this is not done yet because it will only be used for demos
 ==================
*/
int CG_WriteGameState (msg_t *msg, int offset){

	// Write config strings if needed
	if (offset >= GS_CONFIGSTRINGS_FIRST && offset <= GS_CONFIGSTRINGS_LAST){

	}

	// Write baselines if needed
	if (offset >= GS_BASELINES_FIRST && offset <= GS_BASELINES_LAST){

	}

	// Invalid game state request
	glqMsgSystem->WriteLong(msg, -1);
	glqMsgSystem->WriteLong(msg, -1);
	glqMsgSystem->WriteLong(msg, -1);

	return -1;
}

/*
 ==================
 CG_ParseGameState
 ==================
*/
int CG_ParseGameState (msg_t *msg){

	char	string[MAX_STRING_LENGTH];
	int		offset, count, index;

	offset = glqMsgSystem->ReadLong(msg);
	count = glqMsgSystem->ReadLong(msg);
	index = glqMsgSystem->ReadLong(msg);

	// Parse config strings if needed
	if (offset >= GS_CONFIGSTRINGS_FIRST && offset <= GS_CONFIGSTRINGS_LAST){
		offset = index;

		while (count){
			index = glqMsgSystem->ReadShort(msg);
			glqMsgSystem->ReadString(msg, string, sizeof(string));

			if (cgs.configStrings[index])
				glqMemory->Free(cgs.configStrings[index]);

			cgs.configStrings[index] = glqMemory->DupString(string, TAG_CLIENT);

			count--;
		}

		return offset;
	}

	// Parse baselines if needed
	if (offset >= GS_BASELINES_FIRST && offset <= GS_BASELINES_LAST){
		offset = index;

		while (count){
			index = glqMsgSystem->ReadBits(msg, ES_NUMBER_BITS);

			CG_ParseBaseline(msg, &cgs.baselines[index], index);

			count--;
		}

		return offset;
	}

	return -1;
}